package com.swagger.demo.config;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeBindings;
import com.fasterxml.classmate.TypeResolver;
import com.fasterxml.classmate.types.ResolvedInterfaceType;
import com.fasterxml.classmate.types.ResolvedObjectType;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.ser.BeanSerializer;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.google.common.base.Optional;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.SignatureAttribute.ClassSignature;
import javassist.bytecode.SignatureAttribute.ClassType;
import javassist.bytecode.SignatureAttribute.TypeArgument;
import javassist.bytecode.SignatureAttribute.TypeParameter;
import javassist.bytecode.SignatureAttribute.TypeVariable;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.AnnotationMemberValue;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Types;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationModelsProviderPlugin;
import springfox.documentation.spi.service.contexts.RequestMappingContext;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 *
 * 对象使用jackson进行json序列化和反序列化时，能通过@JsonView设置要屏蔽的字段
 * 用@JsonView修饰接口的参数和返回值，在接口请求和响应时，相关字段会被屏蔽
 * 但swagger不能识别@JsonView，在接口文档中，应该被屏蔽的字段仍会展示出来
 * 此代码作用就是让swagger能识别JsonView注解，正确渲染字段
 *
 * 原理：在swagger扫描接口时，用javassist复制原本的参数和返回值的Class生成
 * 新的Class，跳过要忽略的字段，然后把新的Class注册到swagger中
 *
 * 复杂的字段也能复制，支持泛型类、泛型字段、继承的字段、嵌套对象
 *
 * 举个例子：被@JsonView(UserSimpleView.class)修饰的User类有username、
 * birthday、guardian三个字段，其中birthday字段不参与json序列化，就生成
 * 一个新Class叫UserSimpleView_User，UserSimpleView_User只有username、
 * guardian两个字段，用UserSimpleView_User代替User
 *
 *
 * 参考代码：
 * <dependency>
 *     <groupId>com.github.xiaoymin</groupId>
 *     <artifactId>swagger-bootstrap-ui</artifactId>
 *     <version>1.9.6</version>
 * </dependency>
 * 依赖中的OperationDynamicModelProvider类
 *
 * @author honeypie
 *
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 12)
public class OperationJsonViewModelProvider implements OperationModelsProviderPlugin {

    /**
     * 缓存处理后的数据类型，key是接口的url，value是参数和返回值的ResolvedType
     * key的生成规则在下面的代码中描述
     * 在JsonViewRequestModelReader和JsonViewResponseModelReader中读取缓存
     */
    static HashMap<String, ResolvedType> urlModelRefCache = new HashMap<>();

    /**
     * 把Class转换为ResolvedType
     */
    private final TypeResolver typeResolver;
    /**
     * 把Class转换为BeanDescription，BeanDescription能处理多态，整合类的视图和父类属性
     */
    private final SerializationConfig serializationConfig;
    /**
     * 缓存，key是Class，value是Class对应的BeanDescription
     */
    private final HashMap<Class, BeanDescription> beanDescriptionCache;
    /**
     * 分析对象的json serializer
     */
    private final DefaultSerializerProvider serializerProvider;
    /**
     * javassist的classPool
     */
    private final ClassPool classPool;
    /**
     * 缓存，key是新Class的类全名，value是ResolvedType
     * key的生成规则在下面的代码中描述
     */
    private final HashMap<String, ResolvedType> resolvedTypeCache;

    @Autowired
    public OperationJsonViewModelProvider(TypeResolver typeResolver, ObjectMapper objectMapper) {
        urlModelRefCache = new HashMap<>();
        this.typeResolver = typeResolver;
        this.serializationConfig = objectMapper.getSerializationConfig();
        serializerProvider = ((DefaultSerializerProvider) objectMapper.getSerializerProvider()).createInstance(this.serializationConfig, objectMapper.getSerializerFactory());
        classPool = new ClassPool(null);
        classPool.appendSystemPath();
        beanDescriptionCache = new HashMap<>();
        resolvedTypeCache = new HashMap<>();
    }

    @Override
    public void apply(RequestMappingContext context) {
        // pattern是接口url，比如：/addUser
        String pattern = context.getPatternsCondition().getPatterns().toArray()[0].toString();
        // method是请求类型，比如：[POST]
        String method = context.getMethodsCondition().toString();
        // 拼接在一起就是/addUser[POST]
        String requestMappingPattern = pattern + method;

        // 处理返回值
        collectFromReturnType(context, requestMappingPattern);
        // 处理请求参数
        collectParameters(context, requestMappingPattern);
    }

    /**
     * 处理返回值类型
     * @param context context
     * @param requestMappingPattern 接口url
     */
    private void collectFromReturnType(RequestMappingContext context, String requestMappingPattern) {
        ResolvedType returnType = context.getReturnType();
        Optional<JsonView> jsonViewOptional = context.findAnnotation(JsonView.class);
        if (!Types.isVoid(returnType) && jsonViewOptional.isPresent()) {
            // value是JsonView注解的value，是Class对象，相当于视图名称
            Class[] value = jsonViewOptional.get().value();
            if (value.length > 0) {
                // 生成新的返回值类型
                ResolvedType resolvedType = applyJsonViewModelType(returnType, value[0]);
                // 把新的返回值类型添加到context中
                context.operationModelsBuilder().addReturn(resolvedType);
                // 添加缓存，直接使用requestMappingPattern作为key，比如：/addUser[POST]
                urlModelRefCache.put(requestMappingPattern, resolvedType);
            }
        }
    }

    /**
     * 处理参数类型
     * @param context context
     * @param requestMappingPattern 接口url
     */
    private void collectParameters(RequestMappingContext context, String requestMappingPattern) {
        List<ResolvedMethodParameter> parameters = context.getParameters();
        int paramNum = parameters.size();
        // 遍历接口的参数
        for (int i = 0; i < paramNum; i++) {
            ResolvedMethodParameter parameterType = parameters.get(i);
            Optional<JsonView> jsonViewOptional = parameterType.findAnnotation(JsonView.class);
            if (jsonViewOptional.isPresent()) {
                // value是JsonView注解的value，是Class对象，相当于视图名称
                Class[] value = jsonViewOptional.get().value();
                if (value.length > 0) {
                    // 生成新的参数类型
                    ResolvedType resolvedType = applyJsonViewModelType(parameterType.getParameterType(), value[0]);
                    // 把新的参数类型添加到context中
                    context.operationModelsBuilder().addInputParam(resolvedType);
                    // 添加缓存，用url拼接参数index作为key，比如：/addUser[POST]_P[0]
                    String resolvedTypeKey = requestMappingPattern + "_P[" + i + "]";
                    urlModelRefCache.put(resolvedTypeKey, resolvedType);
                }
            }
        }
    }

    /**
     * 根据原类型originType和JsonView视图名称targetView生成新的类型
     *
     * @param originType 原类型
     * @param targetView 视图名称
     * @return 新类型
     */
    private ResolvedType applyJsonViewModelType(ResolvedType originType, Class targetView) {

        // 生成新Class的类名
        String className = getClassNameWithTypeBindings(originType, targetView);
        Class newClass = null;

        Class erasedClass = originType.getErasedType();
        if (checkIgnorableType(erasedClass)) {
            // 先检查缓存
            if (resolvedTypeCache.containsKey(className)) {
                return resolvedTypeCache.get(className);
            }

            try {
                // 生成类名并创建Class
                String erasedClassName = getClassNameWithoutTypeBindings(erasedClass, targetView);
                newClass = generateModelClass(erasedClassName, erasedClass, targetView);
            } catch (CannotCompileException | NotFoundException | NullPointerException e) {
                e.printStackTrace();
            }
        }

        // 检查原类型的泛型，比如：List<User>，List不用管，只需处理User类
        List<ResolvedType> typeParameters = new ArrayList<>();
        for (ResolvedType typeParameter : originType.getTypeBindings().getTypeParameters()) {
            typeParameters.add(applyJsonViewModelType(typeParameter, targetView));
        }

        // 如果原类型是基本类型，则newClass为null，泛型也为空
        if (newClass == null && typeParameters.isEmpty()) {
            return originType;
        }

        // 检查原类型实现的接口，用于生成ResolvedType
        List<ResolvedType> superInterfaces = new ArrayList<>();
        for (ResolvedType anInterface : originType.getImplementedInterfaces()) {
            superInterfaces.add(applyJsonViewModelType(anInterface, targetView));
        }

        TypeBindings typeBindings = TypeBindings.create(erasedClass, typeParameters);
        Class erased = newClass == null ? erasedClass : newClass;

        ResolvedType newType;
        if (originType.findSupertype(Object.class) == null) {
            // 比如originType是List<User>，会走true的逻辑
            newType = new ResolvedInterfaceType(erased, typeBindings, superInterfaces.toArray(new ResolvedType[0]));
        } else {
            // 比如originType是User，会走false的逻辑
            newType = ResolvedObjectType.create(erased, typeBindings, typeResolver.resolve(typeBindings, erased.getSuperclass()), superInterfaces);
        }

        // 生成ResolvedType并填入缓存
        resolvedTypeCache.put(className, newType);
        return newType;
    }

    /**
     * 判断原类型erasedClass是否需要处理
     * 比如：String类型不用管，User类型需要处理
     * @param erasedClass 原类型Class
     * @return 需要处理就返回true，否则返回false
     */
    private boolean checkIgnorableType(Class erasedClass) {
        try {
            JavaType javaType = serializationConfig.constructType(erasedClass);
            if (!(javaType instanceof SimpleType)) {
                return false;
            }
            JsonSerializer<Object> typedValueSerializer = serializerProvider.findTypedValueSerializer(javaType, true, null);
            return typedValueSerializer instanceof BeanSerializer;
        } catch (JsonMappingException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据类名className判断Class是否存在
     * @param className 类名
     * @param erasedClass 原类型
     * @param targetView 视图名称
     * @throws CannotCompileException 异常
     * @throws NotFoundException 异常
     */
    private void prepareModelClass(String className, Class erasedClass, Class targetView) throws CannotCompileException, NotFoundException {
        try {
            // 如果当前线程的classPool中存在，就不用生成
            Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            try {
                // 如果javassist的classPool中存在，就不用生成
                classPool.get(className);
            } catch (NotFoundException e2) {
                // 生成Class
                generateModelClass(className, erasedClass, targetView);
            }
        }
    }

    /**
     * 根据原类型erasedClass和视图名称targetView生成新Class
     * @param className 类全名
     * @param erasedClass 原类型Class
     * @param targetView 视图名称Class
     * @return 新Class
     * @throws CannotCompileException 异常
     * @throws NotFoundException 异常
     */
    private Class generateModelClass(String className, Class erasedClass, Class targetView) throws CannotCompileException, NotFoundException {
        CtClass ctClass;
        try {
            // 判断当前线程的classPool中是否存在
            return Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            // 使用javassist生成新Class
            ctClass = classPool.makeClass(className);
        }

        // 复制原类型的泛型参数
        java.lang.reflect.TypeVariable[] sourceArr = erasedClass.getTypeParameters();
        TypeParameter[] targetArr = new TypeParameter[sourceArr.length];
        for (int i = 0; i < sourceArr.length; i++) {
            targetArr[i] = new TypeParameter(sourceArr[i].getName());
        }
        ctClass.setGenericSignature(new ClassSignature(targetArr).encode());

        // 获取原类型的BeanDescription
        BeanDescription beanDesc = beanDescriptionCache.get(erasedClass);
        if(beanDesc == null) {
            beanDesc = serializationConfig.introspect(serializationConfig.constructType(erasedClass));
            beanDescriptionCache.put(erasedClass, beanDesc);
        }

        ClassFile classFile = ctClass.getClassFile();
        ConstPool constPool = classFile.getConstPool();
        // 复制原类型的ApiModel注解以及参数
        ApiModel apiModel = beanDesc.getClassAnnotations().get(ApiModel.class);
        if (apiModel != null) {
            AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
            Annotation ann = new Annotation(ApiModel.class.getName(), constPool);
            ann.addMemberValue("value", new StringMemberValue(apiModel.value(), constPool));
            ann.addMemberValue("description", new StringMemberValue(apiModel.description(), constPool));
            ann.addMemberValue("parent", new ClassMemberValue(apiModel.parent().getName(), constPool));
            ann.addMemberValue("discriminator", new StringMemberValue(apiModel.discriminator(), constPool));
            ArrayMemberValue targetSubTypes = new ArrayMemberValue(constPool);
            Class<?>[] classes = apiModel.subTypes();
            ClassMemberValue[] sourceSubTypes = new ClassMemberValue[classes.length];
            for (int i = 0; i < classes.length; i++) {
                sourceSubTypes[i] = new ClassMemberValue(classes[i].getName(), constPool);
            }
            targetSubTypes.setValue(sourceSubTypes);
            ann.addMemberValue("subTypes", targetSubTypes);
            ann.addMemberValue("reference", new StringMemberValue(apiModel.reference(), constPool));
            attr.addAnnotation(ann);
            classFile.addAttribute(attr);
        }

        // defaultView是指声明在类上的@JsonView视图名称
        Class<?>[] defaultViews = beanDesc.findDefaultViews();
        boolean hasDefaultView = defaultViews != null && defaultViews.length > 0;

        for (BeanPropertyDefinition property : beanDesc.findProperties()) {
            // 遍历原类型的每一个property，根据视图关系判断是否展示

            boolean propertyVisible = false;

            JsonView JsonViewAnn = property.getAccessor().getAnnotation(JsonView.class);
            if (JsonViewAnn != null) {
                for (Class<?> fieldView : JsonViewAnn.value()) {
                    if (fieldView.isAssignableFrom(targetView)) {
                        propertyVisible = true;
                        break;
                    }
                }
            } else if (hasDefaultView) {
                for (Class<?> defaultView : defaultViews) {
                    if (defaultView.isAssignableFrom(targetView)) {
                        propertyVisible = true;
                        break;
                    }
                }
            }

            if (propertyVisible) {
                // 如果property需要展示，就移植到新Class

                Class fieldType = property.getRawPrimaryType();
                CtClass fieldCtClass;

                if (checkIgnorableType(fieldType)) {
                    String fieldTypeName = getClassNameWithoutTypeBindings(fieldType, targetView);
                    prepareModelClass(fieldTypeName, fieldType, targetView);
                    fieldCtClass = classPool.getCtClass(fieldTypeName);
                } else {
                    fieldCtClass = classPool.getCtClass(fieldType.getName());
                }

                CtField ctField = new CtField(fieldCtClass, property.getName(), ctClass);
                ctField.setModifiers(Modifier.PUBLIC);

                Type fieldGenericType;
                AnnotatedMember member = property.getPrimaryMember();
                if (member instanceof AnnotatedField) {
                    fieldGenericType = ((AnnotatedField) member).getAnnotated().getGenericType();
                } else if (member instanceof AnnotatedMethod) {
                    fieldGenericType = ((AnnotatedMethod) member).getAnnotated().getGenericReturnType();
                } else {
                    continue;
                }

                // 如果字段类型是基本类型，则fieldType.isPrimitive()为true
                if (!fieldType.isPrimitive()) {
                    // 不是基本类型的字段，需要设置GenericSignature
                    ctField.setGenericSignature(getTypeGenericSignature(fieldGenericType, targetView).getType().encode());
                }

                // 复制原字段的ApiModelProperty注解以及参数
                ApiModelProperty apiModelProperty = property.getPrimaryMember().getAnnotation(ApiModelProperty.class);
                if (apiModelProperty != null) {
                    AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
                    Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool);
                    ann.addMemberValue("value", new StringMemberValue(apiModelProperty.value(), constPool));
                    ann.addMemberValue("name", new StringMemberValue(apiModelProperty.name(), constPool));
                    ann.addMemberValue("allowableValues", new StringMemberValue(apiModelProperty.allowableValues(), constPool));
                    ann.addMemberValue("access", new StringMemberValue(apiModelProperty.access(), constPool));
                    ann.addMemberValue("notes", new StringMemberValue(apiModelProperty.notes(), constPool));
                    ann.addMemberValue("dataType", new StringMemberValue(apiModelProperty.dataType(), constPool));
                    ann.addMemberValue("required", new BooleanMemberValue(apiModelProperty.required(), constPool));
                    IntegerMemberValue position = new IntegerMemberValue(constPool);
                    position.setValue(apiModelProperty.position());
                    ann.addMemberValue("position", position);
                    ann.addMemberValue("hidden", new BooleanMemberValue(apiModelProperty.hidden(), constPool));
                    ann.addMemberValue("example", new StringMemberValue(apiModelProperty.example(), constPool));
                    ann.addMemberValue("readOnly", new BooleanMemberValue(apiModelProperty.readOnly(), constPool));
                    EnumMemberValue enumMemberValue = new EnumMemberValue(constPool);
                    enumMemberValue.setValue(apiModelProperty.accessMode().name());
                    enumMemberValue.setType(ApiModelProperty.AccessMode.class.getName());
                    ann.addMemberValue("accessMode", enumMemberValue);
                    ann.addMemberValue("reference", new StringMemberValue(apiModelProperty.reference(), constPool));
                    ann.addMemberValue("allowEmptyValue", new BooleanMemberValue(apiModelProperty.allowEmptyValue(), constPool));
                    ArrayMemberValue targetExtensions = new ArrayMemberValue(constPool);
                    Extension[] extensions = apiModelProperty.extensions();
                    AnnotationMemberValue[] sourceExtensions = new AnnotationMemberValue[extensions.length];
                    for (int i = 0; i < extensions.length; i++) {
                        Extension extension = extensions[i];
                        Annotation ann1 = new Annotation(Extension.class.getName(), constPool);
                        ann1.addMemberValue("name", new StringMemberValue(extension.name(), constPool));
                        ArrayMemberValue targetProperties = new ArrayMemberValue(constPool);
                        ExtensionProperty[] properties = extension.properties();
                        AnnotationMemberValue[] sourceProperties = new AnnotationMemberValue[properties.length];
                        for (int i1 = 0; i1 < properties.length; i1++) {
                            ExtensionProperty extensionProperty = properties[i];
                            Annotation ann2 = new Annotation(ExtensionProperty.class.getName(), constPool);
                            ann2.addMemberValue("name", new StringMemberValue(extensionProperty.name(), constPool));
                            ann2.addMemberValue("value", new StringMemberValue(extensionProperty.value(), constPool));
                            sourceProperties[i] = new AnnotationMemberValue(ann2, constPool);
                        }
                        targetProperties.setValue(sourceProperties);
                        ann1.addMemberValue("properties", targetProperties);
                        sourceExtensions[i] = new AnnotationMemberValue(ann1, constPool);
                    }
                    targetExtensions.setValue(sourceExtensions);
                    ann.addMemberValue("extensions", targetExtensions);
                    attr.addAnnotation(ann);
                    ctField.getFieldInfo().addAttribute(attr);
                }

                ctClass.addField(ctField);
            }
        }

        return ctClass.toClass();
    }

    /**
     * 用javassist生成CtClass时，需要设置CtField类型
     * 根据字段类型type和视图名称targetView生成字段的GenericSignature
     *
     * @param type 对象的字段类型
     * @param targetView 视图名称
     * @return TypeArgument CtField的GenericSignature
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    private TypeArgument getTypeGenericSignature(Type type, Class targetView) throws CannotCompileException, NotFoundException {
        // 字段类型分三种情况处理

        if (type instanceof java.lang.reflect.TypeVariable) {
            // 泛型字段，比如：private T t;
            TypeVariable ctFieldTypeVariable = new TypeVariable(type.getTypeName());
            return new TypeArgument(ctFieldTypeVariable);
        } else if (type instanceof ParameterizedType) {
            // 字段类型包含泛型，比如：private List<User> list;
            ParameterizedType parameterizedType = (ParameterizedType) type;
            TypeArgument rawType = getTypeGenericSignature(parameterizedType.getRawType(), targetView);

            Type[] sourceArr = parameterizedType.getActualTypeArguments();
            TypeArgument[] targetArr = new TypeArgument[sourceArr.length];
            for (int i = 0; i < sourceArr.length; i++) {
                targetArr[i] = getTypeGenericSignature(sourceArr[i], targetView);
            }
            return new TypeArgument(new ClassType(((ClassType) rawType.getType()).getName(), targetArr));
        } else if (type instanceof Class) {
            // 普通类型字段，比如：private User user;
            Class classType = (Class) type;

            String rowTypeName;
            if (checkIgnorableType(classType)) {
                rowTypeName = getClassNameWithoutTypeBindings(classType, targetView);
                prepareModelClass(rowTypeName, classType, targetView);
            } else {
                rowTypeName = classType.getTypeName();
            }

            return new TypeArgument(new ClassType(rowTypeName));
        } else {
            throw new RuntimeException("unknown field type : " + type.getTypeName());
        }
    }

    /**
     * 根据原类型type和视图名称targetView，生成新类型名称，会忽略泛型
     * 比如原类型是com.swagger.demo.entity.User<String>，视图名称是UserSimpleView
     * 新类型名称就是com.swagger.demo.entity.UserSimpleView_User
     *
     * @param type 原类型
     * @param targetView 视图名称
     * @return 新类型名称
     */
    private String getClassNameWithoutTypeBindings(Class type, Class targetView) {
        return type.getPackage().getName() + "." + targetView.getSimpleName() + "_" + type.getSimpleName();
    }

    /**
     * 根据原类型type和视图名称targetView，生成新类型名称，会保留泛型
     * 比如原类型是com.swagger.demo.entity.User<String>，视图名称是UserSimpleView
     * 新类型名称就是com.swagger.demo.entity.UserSimpleView_User<String>
     *
     * @param type 原类型
     * @param targetView 视图名称
     * @return 新类型名称
     */
    private String getClassNameWithTypeBindings(ResolvedType type, Class<?> targetView) {
        StringBuilder className = new StringBuilder();
        className.append(type.getErasedType().getPackage().getName())
                .append(".")
                .append(targetView.getSimpleName())
                .append("_");

        appendTypeName(type, className);
        return className.toString();
    }

    private void appendTypeName(ResolvedType type, StringBuilder className) {
        className.append(type.getErasedType().getSimpleName());
        TypeBindings typeBindings = type.getTypeBindings();
        if (typeBindings.size() > 0) {
            className.append("<");
            int size = typeBindings.size();
            for (int i = 0; i < size; ) {
                appendTypeName(typeBindings.getBoundType(i), className);
                if (++i < size) {
                    className.append(",");
                }
            }
            className.append(">");
        }
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}